/*
 * Copyright (c) 2002-2004 by OpenSymphony All rights reserved.
 */
package cn.org.seventh.web.common;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.fileupload.DefaultFileItem;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author <a href="mailto:young@hrzsoft.cn">Zhou Yang</a>
 * @version $Id: JakartaMultiPartRequest.java 471 2006-03-28 08:08:53Z betty $
 */
public class JakartaMultiPartRequest extends HttpServletRequestWrapper {
    private final transient Log log = LogFactory
            .getLog(JakartaMultiPartRequest.class);

    // maps parameter name -> List of FileItem objects
    private Map files = new HashMap();

    // maps parameter name -> List of param values
    private Map params = new HashMap();

    // any errors while processing this request
    private List errors = new ArrayList();

    public JakartaMultiPartRequest(HttpServletRequest servletRequest,
            String saveDir, int maxSize) throws IOException {
        super(servletRequest);
        DiskFileUpload upload = new DiskFileUpload();

        upload.setSizeThreshold(0);
        upload.setSizeMax(maxSize);
        upload.setHeaderEncoding(servletRequest.getCharacterEncoding());
        if (saveDir == null) {
            saveDir = System.getProperty("java.io.tmpdir");
        }
        if (saveDir != null) {
            File testDir = new File(saveDir);
            if (!testDir.exists() || !testDir.isDirectory()
                    || !testDir.canWrite()) {
                saveDir = System.getProperty("java.io.tmpdir");
            }
        }
        upload.setRepositoryPath(saveDir);

        // Parse the request
        try {
            List items = upload.parseRequest(servletRequest);

            for (int i = 0; i < items.size(); i++) {
                FileItem item = (FileItem) items.get(i);
                log.debug("Found item " + item.getFieldName());
                if (item.isFormField()) {
                    log.debug("Item is a normal form field");
                    List values;
                    if (params.get(item.getFieldName()) != null) {
                        values = (List) params.get(item.getFieldName());
                    } else {
                        values = new ArrayList();
                    }

                    String charset = servletRequest.getCharacterEncoding();
                    if (charset != null) {
                        values.add(item.getString(charset));
                    } else {
                        values.add(item.getString());
                    }
                    params.put(item.getFieldName(), values);
                } else if (item.getSize() == 0) {
                    log.debug("Item is a file upload of 0 size, ignoring");
                } else {
                    log.debug("Item is a file upload");

                    List values;
                    if (files.get(item.getFieldName()) != null) {
                        values = (List) files.get(item.getFieldName());
                    } else {
                        values = new ArrayList();
                    }

                    values.add(item);
                    files.put(item.getFieldName(), values);
                }
            }
        } catch (FileUploadException e) {
            log.error(e);
            errors.add(e.getMessage());
        }
    }

    public Enumeration getFileParameterNames() {
        return Collections.enumeration(files.keySet());
    }

    public String[] getContentTypes(String fieldName) {
        List items = (List) files.get(fieldName);

        if (items == null) {
            return null;
        }

        List contentTypes = new ArrayList(items.size());
        for (int i = 0; i < items.size(); i++) {
            FileItem fileItem = (FileItem) items.get(i);
            contentTypes.add(fileItem.getContentType());
        }

        return (String[]) contentTypes.toArray(new String[contentTypes.size()]);
    }

    public File[] getFiles(String fieldName) {
        List items = (List) files.get(fieldName);

        if (items == null) {
            return null;
        }

        List fileList = new ArrayList(items.size());
        for (int i = 0; i < items.size(); i++) {
            DefaultFileItem fileItem = (DefaultFileItem) items.get(i);
            fileList.add(fileItem.getStoreLocation());
        }

        return (File[]) fileList.toArray(new File[fileList.size()]);
    }

    /**
     * Returns the canonical name of the given file
     */
    private String getCanonicalName(String filename) {
        int forwardSlash = filename.lastIndexOf("/");
        int backwardSlash = filename.lastIndexOf("\\");
        if (forwardSlash != -1 && forwardSlash > backwardSlash) {
            filename = filename.substring(forwardSlash + 1, filename.length());
        } else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
            filename = filename.substring(backwardSlash + 1, filename.length());
        }

        return filename;
    }

    public String[] getFileNames(String fieldName) {
        List items = (List) files.get(fieldName);

        if (items == null) {
            return null;
        }

        List fileNames = new ArrayList(items.size());
        for (int i = 0; i < items.size(); i++) {
            DefaultFileItem fileItem = (DefaultFileItem) items.get(i);
            fileNames.add(getCanonicalName(fileItem.getName()));
        }

        return (String[]) fileNames.toArray(new String[fileNames.size()]);
    }

    public String[] getFileSystemNames(String fieldName) {
        List items = (List) files.get(fieldName);

        if (items == null) {
            return null;
        }

        List fileNames = new ArrayList(items.size());
        for (int i = 0; i < items.size(); i++) {
            DefaultFileItem fileItem = (DefaultFileItem) items.get(i);
            fileNames.add(fileItem.getStoreLocation().getName());
        }

        return (String[]) fileNames.toArray(new String[fileNames.size()]);
    }

    public String getParameter(String name) {
        List v = (List) params.get(name);
        if (v != null && v.size() > 0) {
            return (String) v.get(0);
        }

        return super.getParameter(name);
    }

    public Enumeration getParameterNames() {
        return mergeParams(Collections.enumeration(params.keySet()), super
                .getParameterNames());
    }

    public String[] getParameterValues(String name) {
        List v = (List) params.get(name);
        if (v != null && v.size() > 0) {
            return (String[]) v.toArray(new String[v.size()]);
        }

        return null;
    }

    public List getErrors() {
        return errors;
    }

    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterMap()
     */
    public Map getParameterMap() {
        Map map = new HashMap();
        Enumeration enumeration = getParameterNames();

        while (enumeration.hasMoreElements()) {
            String name = (String) enumeration.nextElement();
            map.put(name, this.getParameterValues(name));
        }

        return map;
    }

    /**
     * Returns <tt>true</tt> if any errors occured when parsing the HTTP
     * multipart request, <tt>false</tt> otherwise.
     *
     * @return <tt>true</tt> if any errors occured when parsing the HTTP
     *         multipart request, <tt>false</tt> otherwise.
     */
    public boolean hasErrors() {
        if ((errors == null) || errors.isEmpty()) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Adds an error message.
     *
     * @param anErrorMessage
     *            the error message to report.
     */
    protected void addError(String anErrorMessage) {
        if (errors == null) {
            errors = new ArrayList();
        }

        errors.add(anErrorMessage);
    }

    /**
     * Merges 2 enumeration of parameters as one.
     *
     * @param params1
     *            the first enumeration.
     * @param params2
     *            the second enumeration.
     * @return a single Enumeration of all elements from both Enumerations.
     */
    protected Enumeration mergeParams(Enumeration params1, Enumeration params2) {
        Vector temp = new Vector();

        while (params1.hasMoreElements()) {
            temp.add(params1.nextElement());
        }

        while (params2.hasMoreElements()) {
            temp.add(params2.nextElement());
        }

        return temp.elements();
    }

}
